import type ITableColumn from './types/ITableColumn'
import type ITableData from './types/ITableData'
import type ITableOptions from './types/ITableOptions'

export class CanvasTable {
  private ctx: CanvasRenderingContext2D

  private options: ITableOptions
  private data: ITableData[]
  // 从外层传入的列数组
  private columns: ITableColumn[] = []
  // 扁平化后的列数组
  private flattenColumns: ITableColumn[] = []
  // 固定左侧列标题配置
  private fixedLeftColumns: ITableColumn[] = []
  // 普通的表格列配置
  private normalColumns: ITableColumn[] = []
  // 固定右侧列标题配置
  private fixedRightColumns: ITableColumn[] = []
  // 表格标题层级
  private headerLevels: number = 1
  // 表格状态
  private state: 'loading' | 'empty' | 'error' | 'success' = 'empty'
  // 在无表格数据时的提示语：loading 时、空数据时、或者错误时
  private tip: string = '暂无数据'
  // 横向滚动条位置
  private scrollLeft = 0
  private scrollXWidth = 0
  // 纵向滚动条位置
  private scrollTop = 0
  private scrollYHeight = 0
  // 排序配置
  private orderBy: string = ''
  private isAsc: boolean = true
  // 表格边框配置
  private borderWidth: number = 0
  private borderColor: string = '#ccc'
  private borderStyle: string = 'solid'

  /**
   *
   * @param ctx canvas对应的2D Context对象
   * @param data 需要在Canvas上绘制的数据数组
   * @param columns 表格列配置对象
   * @param options 表格的其他配置项
   */
  constructor(
    ctx: CanvasRenderingContext2D,
    data: ITableData[],
    columns: ITableColumn[],
    opt: ITableOptions
  ) {
    this.ctx = ctx
    this.ctx.direction = 'ltr'
    this.options = Object.assign(
      {
        background: '#fff',
        border: '1px solid #ccc',
        headerColor: '#000',
        headerFontSize: 16,
        headerFontWeight: 'bold',
        rowHeight: 32,
        rowColor: '#000',
        rowFontSize: 14,
        rowFontWeight: 'normal',
        scrollXHeight: 10,
        scrollYWidth: 10,
        sortWidth: 10,
        sortActiveColor: '#000',
        sortInactiveColor: '#ccc',
      },
      opt,
      {}
    )
    this.data = data
    if (this.data && this.data.length) {
      this.state = 'success'
      this.tip = ''
    } else {
      this.state = 'empty'
      this.tip = '暂无数据'
    }
    this.setColumns(columns)
    this.scrollLeft = 0
    this.scrollTop = 0
    // 排序
    this.orderBy = this.options.orderBy || ''
    this.isAsc = this.options.isAsc || true
    // 边框
    if (this.options.border) {
      const arr = this.options.border.split(' ')
      for (let i = 0; i < arr.length; i++) {
        const element = arr[i]
        if (element === 'none') {
          this.borderWidth = 0
          break
        } else if (element.includes('px')) {
          this.borderWidth = parseInt(element)
        } else if (element.includes('#')) {
          this.borderColor = element
        } else if (['solid', 'dotted', 'dashed'].includes(element)) {
          this.borderStyle = element
        }
      }
    }
  }

  /**
   * 设置表格状态
   * @param state 表格状态
   * @param tip 提示词
   */
  public setState(
    state: 'loading' | 'empty' | 'error' | 'success' = 'loading',
    tip = '加载中...',
    data?: ITableData[]
  ) {
    if (
      state !== this.state ||
      tip !== this.tip ||
      (data && data !== this.data)
    ) {
      this.state = state
      this.tip = tip
      this.data = data || []
      this.draw()
    }
  }
  /**
   * 将初始的列配置数组分类、扁平化、计算列宽度
   * @param columns 外层传入的初始列配置数组
   */
  public setColumns(columns: ITableColumn[]) {
    this.columns = columns
    this.fixedLeftColumns = columns.filter(
      (col) => col.fixed === 'left' && typeof col.left !== 'undefined'
    )
    this.fixedRightColumns = columns.filter(
      (col) => col.fixed === 'right' && typeof col.right !== 'undefined'
    )
    this.normalColumns = columns.filter((col) => !col.fixed)
    this.flattenColumns = columns.reduce((prev, curr) => {
      if (curr.children && curr.children.length > 0) {
        return [...prev, ...curr.children]
      }
      return [...prev, curr]
    }, [] as ITableColumn[])
    this.headerLevels = 1
    columns.forEach((column) => {
      if (column.children && column.children.length > 0) {
        this.headerLevels = 2
        column.children.forEach((child) => {
          if (child.children && child.children.length > 0) {
            this.headerLevels = 3
            child.children.forEach((grandchild) => {
              if (grandchild.children && grandchild.children.length > 0) {
                this.headerLevels = 4
                this.flattenColumns.push(...grandchild.children)
              } else {
                this.flattenColumns.push(grandchild)
              }
            })
          } else {
            this.flattenColumns.push(child)
          }
        })
      } else {
        this.flattenColumns.push(column)
      }
    })
  }

  public drawTable(columns: ITableColumn[], data: ITableData[]) {
    // TODO: Implement table drawing logic
    if (!this.ctx) return
    const { clearRect, canvas } = this.ctx || {}
    clearRect && clearRect(0, 0, canvas?.width || 0, canvas?.height || 0)
    this.options.background && (this.ctx.fillStyle = this.options.background)
    // 先画数据再画标题
    this.drawBody(data || this.data)
    this.drawHeader()
    this.drawXScroll()
    this.drawYScroll()
  }
  /**
   * 在Canvas上绘制表头
   * @param columns 表格列配置对象
   */
  public drawHeader() {
    if (!this.ctx) return
    const {
      headerColor,
      headerFontSize = 16,
      headerFontWeight,
      scrollYWidth,
      scrollXHeight,
    } = this.options
    // TODO: Implement header drawing logic
    headerColor && (this.ctx.fillStyle = headerColor)
    this.ctx.font = `${headerFontWeight} ${headerFontSize}px Arial`
    let x = 0 + scrollYWidth
    let y = 0 + scrollXHeight

    // 绘制左侧固定列
    this.fixedLeftColumns.forEach((col) => {
      this.drawHeaderCell(col, x, y)
      x += col.width
    })
    // 绘制正常列
    x -= this.scrollLeft
    this.normalColumns.forEach((col) => {
      this.drawHeaderCell(col, x, y)
      x += col.width
    })
    // 绘制右侧固定列
    this.fixedRightColumns.forEach((col) => {
      this.drawHeaderCell(col, x, y)
      x += col.width
    })
    // 绘制标题下方的阴影
    if (this.borderWidth) {
    }
  }

  public drawHeaderCell(column: ITableColumn, x: number, y: number) {
    const {
      rowHeight,
      headerFontWeight,
      headerFontSize,
      headerColor,
      sortWidth = 10,
    } = this.options
    const {
      children,
      sortable,
      color,
      width,
      headerAlign = 'left',
      title,
    } = column
    const hasChildren = children && children.length
    const maxWidth = !hasChildren && sortable ? width - sortWidth : width
    // 边框
    if (this.borderWidth) {
      this.ctx.lineWidth = this.borderWidth
      this.ctx.strokeStyle = this.borderColor
      this.ctx.strokeRect(x, y, width, rowHeight)
    }
    // 文字
    this.ctx.fillStyle = color || headerColor || '#000'
    this.ctx.textAlign = headerAlign || 'left'
    this.ctx.font = `${headerFontWeight} ${headerFontSize}px Arial`
    this.ctx.fillText(
      title,
      headerAlign === 'right'
        ? x + maxWidth
        : headerAlign === 'center'
        ? x + maxWidth / 2
        : x,
      y,
      maxWidth
    )

    if (!hasChildren) {
      x += width
      // 没有子列，绘制排序区域
      sortable && this.drawSort(column, x - sortWidth, y, sortWidth)
    } else {
      // 绘制子列
      ;(children || []).forEach((child) => {
        this.drawHeaderCell(child, x, y + rowHeight)
        x += child.width
      })
    }
  }
  /**
   * 在Canvas上绘制表格内容
   * @param data 需要在Canvas上绘制的数据数组
   */
  public drawBody(data: ITableData[]) {
    // TODO: Implement cell drawing logic
    if (this.data !== data) {
      this.data = data

      let y = this.options.rowHeight || 32
      ;(data || []).forEach((row, rowIndex) => {
        this.drawRow(row, rowIndex)
      })
    }
  }

  public drawCell(column: ITableColumn, row: ITableData, x: number, y: number) {
    // 样式
    const { rowFontSize = 14, rowFontWeight, rowHeight } = this.options
    const value = this.formatter(column, row[column.prop])

    // 绘制矩形（边框）
    if (this.borderWidth) {
      this.ctx.lineWidth = this.borderWidth
      this.ctx.strokeStyle = this.borderColor
      this.ctx.strokeRect(x, y, column.width, rowHeight)
    }
    // 绘制内容
    let color = this.getCellColor(row, column)
    color && (this.ctx.fillStyle = color)
    this.ctx.font = `${rowFontWeight} ${rowFontSize}px Arial`
    this.ctx.fillText(
      value,
      column.align === 'right'
        ? x + column.width
        : column.align === 'center'
        ? x + column.width / 2
        : x,
      y,
      column.width
    )
  }

  /**
   * 绘制表头列的排序区域
   * @param column 列配置对象
   * @param x
   * @param y
   */
  public drawSort(
    column: ITableColumn,
    x: number,
    y: number,
    width: number = 10
  ) {
    // TODO: Implement sort drawing logic
    // 绘制第一个三角形
    // 绘制第二个三角形（倒）
  }

  public drawRow(row: ITableData, rowIndex: number = 0) {
    const { rowHeight } = this.options
    let x = 0
    let y = rowIndex * rowHeight
    const columns = [
      ...this.fixedLeftColumns,
      ...this.normalColumns,
      ...this.fixedRightColumns,
    ]
    columns.forEach((col) => {
      if (col.children && col.children.length) {
        col.children.forEach((child) => {
          if (child.children && child.children.length) {
            child.children.forEach((grandchild) => {
              this.drawCell(grandchild, row, x, y)
              x += grandchild.width
            })
          } else {
            this.drawCell(child, row, x, y)
            x += child.width
          }
        })
      } else {
        this.drawCell(col, row, x, y)
        x += col.width
      }
    })
  }
  /**
   * 绘制横向滚动条
   */
  public drawXScroll() {
    // TODO: Implement x-scroll drawing logic
  }
  /**
   * 绘制纵向滚动条
   */
  public drawYScroll() {
    // TODO: Implement y-scroll drawing logic
  }
  /**
   * 返回三角形的三个顶点坐标
   * @param x 三角形中心点的x轴坐标
   * @param y 三角形中心点的y轴坐标
   * @param radius 三角形中心点到顶点的距离
   */
  private getTrianglePoints(
    x: number,
    y: number,
    radius: number
  ): Array<{ x: number; y: number }> {
    const array = []
    for (let i = 0; i < 3; i++) {
      // 三角形的三个角坐标：左下 右下 正上
      const theta = (i * 120 * Math.PI) / 180 + Math.PI / 6
      array.push({
        x: x + Math.cos(theta) * radius,
        y: y + Math.sin(theta) * radius,
      })
    }
    return array
  }
  /**
   * 绘制圆角三角形
   * @param x 三角形中心x轴坐标
   * @param y 三角形中心y轴坐标
   * @param triangleRadius 三角形中心到顶点的距离
   * @param roundRadius 三角形的角圆形半径
   * @param fillColor 填充颜色
   */
  private drawRoundTriangle(
    x: number,
    y: number,
    triangleRadius: number,
    roundRadius: number,
    fillColor: string = '#000'
  ) {
    const points = this.getTrianglePoints(x, y, triangleRadius)
    const leftPoints = this.getTrianglePoints(
      points[0].x,
      points[0].y,
      roundRadius
    )
    const rightPoints = this.getTrianglePoints(
      points[1].x,
      points[1].y,
      roundRadius
    )
    const topPoints = this.getTrianglePoints(
      points[2].x,
      points[2].y,
      roundRadius
    )

    this.ctx.beginPath()
    this.ctx.moveTo(topPoints[0].x, topPoints[0].y)
    this.ctx.lineTo(leftPoints[2].x, leftPoints[2].y)
    this.ctx.arcTo(
      leftPoints[0].x,
      leftPoints[0].y,
      leftPoints[1].x,
      leftPoints[1].y,
      roundRadius
    )
    this.ctx.lineTo(rightPoints[0].x, rightPoints[0].y)
    this.ctx.arcTo(
      rightPoints[1].x,
      rightPoints[1].y,
      rightPoints[2].x,
      rightPoints[2].y,
      roundRadius
    )
    this.ctx.lineTo(topPoints[1].x, topPoints[1].y)
    this.ctx.arcTo(
      topPoints[2].x,
      topPoints[2].y,
      topPoints[0].x,
      topPoints[0].y,
      roundRadius
    )
    this.ctx.closePath()
    this.ctx.fillStyle = fillColor
    this.ctx.fill()
  }

  public drawBorder() {
    // TODO: Implement border drawing logic
    if (!this.borderWidth) return
    this.ctx.lineWidth = this.borderWidth
    this.ctx.rect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
  }

  public draw(columns?: ITableColumn[], data?: ITableData[]) {
    if (this.state !== 'success') {
      // 绘制非成功状态的表格
      this.drawHeader()
      // 绘制其他内容
      return
    }
    // 完整的表格绘制
    this.drawTable(columns || this.columns, data || this.data)
  }

  public updateData(data: ITableData[]) {
    this.data = data
  }

  public updateColumns(columns: ITableColumn[]) {
    this.columns = columns
  }

  public updateOptions(options: ITableOptions) {
    // TODO: Implement options update logic
    this.options = Object.assign(this.options, options)
  }
  /**
   * 获取单元格要显示的内容
   * @param column 列配置对象
   * @param value 单元格值
   * @returns 单元格显示内容
   */
  private formatter(column: ITableColumn, value: any) {
    if (column.formatter) {
      return column.formatter(value)
    }
    return value === 0 ? '0' : value || '--'
  }

  private getCellColor(row: ITableData, column: ITableColumn) {
    // TODO: Implement cell color logic
    const { prop, color } = column
    const { rowColor } = this.options
    if (row[`${prop}Color`]) return row[`${prop}Color`]
    return color || rowColor
  }
}
